package com.witsight.hibernate.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import javax.persistence.Convert;

import org.hibernate.HibernateException;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyChainedImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyFieldImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.transform.BasicTransformerAdapter;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.descriptor.java.EnumJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import com.witsight.hibernate.helper.ConvertEnumJavaTypeDescriptor;
import com.witsight.platform.model.BoEntity;
import com.witsight.platform.util.lang.ClassUtil;
import com.witsight.platform.util.lang.StringUtil;

/**   
 * 说明：自定义别名结果转换
 * @Title: CustomAliasToBeanResultTransformer.java 
 * @Package com.witsight.hibernate.dao 
 * @See: {@link ResultTransformer}
 * 		 {@link Serializable}
 * Copyright: Copyright (c) 2017
 * Company:sany huax witsight team by product
 * @author: fuyp  
 * @date: 2017年9月18日 上午8:30:59 
 * @version: V1.0
 *
 */
public class CustomAliasToBeanResultTransformer extends BasicTransformerAdapter implements Serializable {
	private static final long serialVersionUID = -6578259622757836676L;
	protected final Logger log = LoggerFactory.getLogger(CustomAliasToBeanResultTransformer.class);
	
	private final Class<?> resultClass;
	private boolean isInitialized;
	private String[] aliases;
	private Setter[] setters;
	private JavaTypeDescriptor<?>[] types;
	
	public CustomAliasToBeanResultTransformer(Class<?> resultClass) {
		if (resultClass == null) {
			throw new IllegalArgumentException("resultClass cannot be null");
		}
		this.isInitialized = false;
		this.resultClass = resultClass;
	}
	
	private Map transformMap(Object[] tuple) {
		Map result = new HashMap(tuple.length);
		for ( int i=0; i<tuple.length; i++ ) {
			String alias = aliases[i];
			if ( alias!=null ) {
				result.put( alias, tuple[i] );
			}
		}
		return result;
	}
	
	private BoEntity transformBoEntity(Object[] tuple) {
		BoEntity result = new BoEntity(tuple.length);
		for ( int i=0; i< tuple.length; i++ ) {
			String alias = this.aliases[i];
			if ( alias != null ) {
				result.put(alias, tuple[i] );
			}
		}
		return result;
	}
	
	@Override
	public Object transformTuple(Object[] tuple, String[] aliases) {
		Object result;

		try {
			if ( ! isInitialized ) {
				initialize( aliases );
			}
			
			if (ClassUtil.isAssignable(resultClass, BoEntity.class)) {
				return transformBoEntity(tuple);
			} else if (ClassUtil.isAssignable(resultClass, Map.class)){
				return transformMap(tuple);
			}
			
			result = resultClass.newInstance();
			for ( int i = 0; i < aliases.length; i++ ) {
				if ( setters[i] != null ) {
					this.setters[i].set(result, this.types[i].wrap(tuple[i], null), null);
				}
			}
		}
		catch ( InstantiationException e ) {
			throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
		}
		catch ( IllegalAccessException e ) {
			throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
		}

		return result;
	}
	private void check(String[] aliases) {
		if ( ! Arrays.equals( aliases, this.aliases ) ) {
			throw new IllegalStateException(
					"aliases are different from what is cached; aliases=" + Arrays.asList( aliases ) +
							" cached=" + Arrays.asList( this.aliases ) );
		}
	}
	/**
	 * 
	 * 说明：转换字段名到属性值上面
	 * @Title: convertColumnToProperty 
	 * @See: 转换字段名到属性值上面
	 * @author fuyp 
	 * @param columnName 字段名
	 * @return 
	 * @date: 2015年4月22日 下午3:51:16
	 */
	private String convertColumnToProperty(String columnName) {
		if (StringUtil.isEmpty(columnName)) {
			return "";
		}
		columnName = columnName.toLowerCase();
		StringBuffer buff = new StringBuffer(columnName.length());
		StringTokenizer st = new StringTokenizer(columnName, "_");
		while (st.hasMoreTokens()) {
			buff.append(StringUtil.capitalize(st.nextToken()));
		}

		buff.setCharAt(0, Character.toLowerCase(buff.charAt(0)));
		return buff.toString();
	}
	
	/**
	 * 
	 * 说明：初始化
	 * @Title: initialize
	 * @See: 初始化
	 * @author fuyp 
	 * @param aliases 别名集合
	 * @date: 2015年4月22日 下午3:56:14
	 */
	private void initialize(String[] aliases) {
		PropertyAccessStrategyChainedImpl propertyAccessStrategy = new PropertyAccessStrategyChainedImpl(
				PropertyAccessStrategyBasicImpl.INSTANCE,
				PropertyAccessStrategyFieldImpl.INSTANCE,
				PropertyAccessStrategyMapImpl.INSTANCE
		);
		
		this.aliases = new String[aliases.length];
		this.setters = new Setter[aliases.length];
		this.types = new JavaTypeDescriptor[aliases.length];
		for (int i = 0; i < aliases.length; ++i) {
			String alias = convertColumnToProperty(aliases[i]);
			if (alias != null) {
				this.aliases[i] = alias;
				try {
					PropertyAccess propertyAccess = propertyAccessStrategy.buildPropertyAccess( resultClass, alias );
					Field field = ReflectionUtils.findField(this.resultClass, alias);
					this.setters[i] = propertyAccess.getSetter();
					this.types[i] = JavaTypeDescriptorRegistry.INSTANCE.getDescriptor(field.getType());
					if (this.types[i] instanceof EnumJavaTypeDescriptor) {
						Convert convert = field.getAnnotation(Convert.class);
						if (convert == null) {
							convert = propertyAccess.getGetter().getMethod().getAnnotation(Convert.class);
						}
						this.types[i] = new ConvertEnumJavaTypeDescriptor(field.getType(), convert);
					}
				} catch (Exception e) {
					if (this.log.isDebugEnabled()) {
						this.log.debug(e.getMessage());
					}
					this.setters[i] = null;
				}
				if (this.types[i] == null) {
					this.setters[i] = null;
				}
			}
		}
		this.isInitialized = true;
	}
	
	@Override
	public boolean equals(Object o) {
		if ( this == o ) {
			return true;
		}
		if ( o == null || getClass() != o.getClass() ) {
			return false;
		}

		CustomAliasToBeanResultTransformer that = ( CustomAliasToBeanResultTransformer ) o;

		if ( ! resultClass.equals( that.resultClass ) ) {
			return false;
		}
		if ( ! Arrays.equals( aliases, that.aliases ) ) {
			return false;
		}

		return true;
	}

	@Override
	public int hashCode() {
		int result = resultClass.hashCode();
		result = 31 * result + ( aliases != null ? Arrays.hashCode( aliases ) : 0 );
		return result;
	}
}
